local printd  = debug.print or print
local assert  = debug.assert or assert

module(...,package.seeall)

local state_mt = {
  __type ="state",
  handle = function(...)
    printd("state has no handler with args",...)
  end,
  __index = function(t,k)
    return getmetatable(t)[k]
  end, 
}

local statesystem_mt = {
    top = function(self)
        return self.statestack[#self.statestack]
    end,
    push = function(self,state) 
      assert(state,"pushed state is nil")
      local top = self:top()
      if top then 
        printd("pushed "..top.__name.." on to the stack")
        top:handle("onpush")
      end    
      table.insert(self.statestack,state)
    end, 
    pop = function(self)
      local top = self:top()
      if top then 
        printd("poped "..top.__name.." on to the stack")
        top:handle("onpop")
        table.remove(self.statestack)
      end    
    end,    
    change = function(self,state) 
      self:pop()
      self:push(state)
    end,
    handle = function(self,event,...)
      if event == "pushstate" then
        self:push(...)
        return 
      elseif event == "popstate" then
        self:pop()
        return 
      elseif event == "changestate" then
        self:change(...)
        return 
      end
      local top = self:top()
      if top then     
        return top:handle(event,...)
      else
        printd("node doesn't have a state")  
      end  
    end,    
    __index = function(t,k)
      return getmetatable(t)[k]
    end
}

new = function(name,callback)
  local s = {}
  s.__name = name
  s.handle = callback   
  setmetatable(s,state_mt)
  return s
end

newsystem = function(statestack)
  local ss = {}
  ss.statestack = statestack or {}
  setmetatable(ss,statesystem_mt)
  return ss
end

if __UNIT_TEST__ then
  unittest.start()
  
    teststate = new( "A",    -->state name
                      print  -->handle callback
                   )
    ss = newsystem({teststate})
    
  unittest.stop()
end

